# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import time
import sys
import json

import coin.exchange.base.kr_rest.level_book as level_book_mdl
import coin.exchange.base.kr_rest.public_client_base as pubcb
import coin.exchange.gemini_v1.kr_rest.native_public_client as npubc
from coin.exchange.gemini_v1.kr_rest.constants import active_symbol_list
from coin.exchange.gemini_v1.kr_rest.product import GeminiProduct
from coin.exchange.base.kr_rest.rest_client_base import RestUpdate

from coin.proto.coin_market_query_pb2 import ProductTicker, ExchangeTicker
from coin.exchange.gemini_v1.kr_rest.error_code import gemini_strerror


class GeminiFeedParser(object):
  @staticmethod
  def parse_native_ticker(update_msg, product):
    """ sample response message
    {
      "bid": "10105.58",
      "ask": "10105.59",
      "volume": {
        "BTC": "7973.3490633884",
        "USD": "79966404.284360493171",
        "timestamp": 1517466300000
      },
      "last": "10105.59"
    }"""
    return ProductTicker(last=float(update_msg['last']),
                         ask=float(update_msg['ask']),
                         bid=float(update_msg['bid']),
                         volume_24hr=float(update_msg['volume'][product.base]),
                         exchange_timestamp=int(float(update_msg['volume']['timestamp']) * 1e+6),
                         native_symbol=product.native_symbol,
                         symbol=product.symbol)

  @staticmethod
  def parse_level_book(update_msg, product):
    """ sample response message
    {
      "bids": [
        { "price": "10124.59", "amount": "0.29630829", "timestamp": "1517466187" },
        { "price": "10124.58", "amount": "11.16569536", "timestamp": "1517466187" },
        { "price": "10104.50", "amount": "1.7745", "timestamp": "1517466187" }
      ],
      "asks": [
        { "price": "10124.70", "amount": "0.00074053", "timestamp": "1517466187" },
        { "price": "10124.71", "amount": "0.00192593", "timestamp": "1517466187" },
        { "price": "10124.72", "amount": "0.00182693", "timestamp": "1517466187" }
      ]
    }"""
    level_book = level_book_mdl.LevelBook(product)

    for elem in update_msg['asks']:
      level_elem = level_book_mdl.LevelElement(float(elem['price']), float(elem['amount']), 0)
      level_book.add_ask(level_elem)

    for elem in update_msg['bids']:
      level_elem = level_book_mdl.LevelElement(float(elem['price']), float(elem['amount']), 0)
      level_book.add_bid(level_elem)
    level_book.finalize()
    return level_book


class GeminiPublicClient(pubcb.PublicClientBase):
  def __init__(self, timeout=1):
    self.npubc = npubc.GeminiNativePublicClient(timeout=timeout)

  @staticmethod
  def _check_update(update):
    if update.status is not None and update.status != 200:
      sys.stderr.write("%s\n" % gemini_strerror(update.status))
      sys.stderr.write("%s\n" % json.dumps(update.msg, indent=2))
      raise Exception('Query response has error!')

  def query_ticker_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_pubticker(symbol)
    self._check_update(update)
    update.msg = GeminiFeedParser.parse_native_ticker(update.msg, product)
    return update

  def query_exchange_ticker_impl(self):
    raw_tickers = []
    product_ticker_list = []
    req_time = time.time()
    for symbol in active_symbol_list:
      update = self.npubc.get_pubticker(symbol)
      self._check_update(update)
      raw_tickers.append(update.msg)
      product = GeminiProduct.FromStr(symbol)
      product_ticker_list.append(GeminiFeedParser.parse_native_ticker(update.msg, product))
    res_time = time.time()

    exchange_ticker = ExchangeTicker(exchange='Gemini', each_ticker=product_ticker_list)
    update = RestUpdate(req_time, res_time, raw_tickers)
    update.msg = exchange_ticker
    return update

  def query_level_book_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_book(symbol)
    self._check_update(update)
    update.msg = GeminiFeedParser.parse_level_book(update.msg, product)
    return update
